package com.woniuxy.gateway.filter;

import com.alibaba.nacos.api.common.ResponseCode;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.woniuxy.commons.enums.ResultCodeEnum;
import com.woniuxy.commons.enums.TokenEnum;
import com.woniuxy.commons.result.Result;
import com.woniuxy.commons.utils.JWTUtil;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;

/**
 * <P> @Author: ideaAdmin
 * <P> TODO: Class
 * <P> TODO: 认证过滤器 GlobalFilter是pre的
 * <P> Date: 2022/3/29:14:16
 * <P> Description:
 */
@Component
public class AuthFilter implements GlobalFilter, Ordered {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * TODO: 过滤器执行过滤代码
     *      参数1：交换机 转换信息
     *      参数2：过滤器
     **/
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("过滤器执行。。。");

        //TODO: 1.得到url
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        String url = request.getPath().toString().intern();
        System.out.println("经过过滤器的url" + url);
        //TODO: 2.判断url 是否需要认证
        if (requireAuth(url)) {
            //TODO: 3.如果需要认证,判断是否认证过

            List<String> tokens = request.getHeaders().get("authorization");
            List<String> refreshTokens = request.getHeaders().get("refreshToken");

            //TODO: 3.1 判断token、refreshToken是否为null

            if (tokens == null || refreshTokens == null) {
                return goLogin(response);
            }

            if ("null".equals(tokens.get(0)) || "null".equals(refreshTokens.get(0))) {
                return goLogin(response);
            }

            //TODO: 3.2 判断 token、refreshToken是否为null
            //TODO: 3.2.1 判断 refreshToken
            if (!redisTemplate.hasKey(refreshTokens.get(0))) {
                //不包含：过期 伪造
                //去登陆
                return goLogin(response);
            }
            //TODO: 3.2.2 判断 token


            //TODO: 4.先获取token、refreshToken,然后判断是否能获取到

            TokenEnum tokenEnum = JWTUtil.verify(tokens.get(0));

            //TODO: 5.判断token、refreshToken(redis) 是否合法
            if (TokenEnum.TOKEN_BAD.equals(tokenEnum)) {
                //非法
                System.out.println("token非法");
                return goLogin(response);
            }

            //TODO: 6.token是否过期 如果过期重新生成token 返回给浏览器
            if (TokenEnum.TOKEN_EXPIRE.equals(tokenEnum)) {
                //过期
                System.out.println("token过期");


                //重新生成token  redis中token更新 将token返回浏览器
                //从原来的token获取用户的账号 生成新的token
                String token = JWTUtil.generateToken
                        (JWTUtil.getUname(tokens.get(0)));

                //更新redis的key
                redisTemplate.opsForHash().put(refreshTokens.get(0), "token", token);

                //将新token放到响应头中返回
                response.getHeaders().
                        add("authorization", token);

                //暴露头
                response.getHeaders().
                        add("Access-Control-Expose-Headers",
                                "authorization,refreshToken");
            }


        }

        //放行
        return chain.filter(exchange);
    }

    /**
     * <P>Author ideaAdmin
     * <P>TODO:返回去登录的方法
     * <P>Date 11:24 2022/3/30
     * <P>* @param response   返回体
     * <P>* @return reactor.core.publisher.Mono<java.lang.Void>
     **/
    private Mono<Void> goLogin(ServerHttpResponse response) {
        //需要去认证

        DataBuffer dataBuffer = null;

        try {
            //TODO 转换成json数据返回
            byte[] bytes = new ObjectMapper().
                    writeValueAsString(Result.build(null, ResultCodeEnum.NO_LOGIN))
                    .getBytes();

            //TODO 封装数据
            dataBuffer = response.bufferFactory().wrap(bytes);

            //TODO 设置响应头
            response.getHeaders().add("Content-type",
                    "application/json;charset=utf-8");


        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        //TODO  返回数据
        return response.writeWith(Mono.just(dataBuffer));
    }

    /**
     * TODO: 指定当前过滤器的执行顺序
     **/
    @Override
    public int getOrder() {
        return 0;
    }

    /**
     * <P>Author ideaAdmin
     * <P>TODO: 判断url是否需要认证
     * <P>Date 10:24 2022/3/30
     * <P>* @param url
     * <P>* @return boolean
     **/
    public boolean requireAuth(String url) {
        //需要、不需要的认证的url都有应该放到数据库中，不应该在程序中写死，维护不方便
        List<String> urls = Arrays.asList("/auth/user/login", "/auth/register");

        //TODO 如果有不匹配的 返回true 否则都匹配 返回false
        for (String myUrl : urls) {
            if (myUrl.equals(url)) {
                return false;
            }
        }
        return true;
    }
}
